/*
 * File:   benchmark_main.cpp
 * Copyright (c) 2010. Silviu Caragea <silviu@intelliproject.net>
 * Created on October 6, 2010, 12:04 AM
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  1.Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  2.Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *  3.All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *       This product includes software developed by the University of
 *       California, Berkeley and its contributors.
 *  4.Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
    This file is used to compare performances between: litevent and libevent
 */


#define	timersub(tvp, uvp, vvp)						\
	do {								\
		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;		\
		(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;	\
		if ((vvp)->tv_usec < 0) {				\
			(vvp)->tv_sec--;				\
			(vvp)->tv_usec += 1000000;			\
		}							\
	} while (0)

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>



#ifndef WIN32
    #include <sys/time.h>
    #include <sys/socket.h>
    #include <sys/signal.h>
    #include <sys/resource.h>
    #include <unistd.h>
#else
    #include "socketpair.cpp"
    #pragma comment(lib,"ws2_32.lib")
#endif

static int count, writes, fired;
static int *pipes;
static int num_pipes, num_active, num_writes;

#define LIBEVENT

#ifdef LIBEVENT

#include <event.h>

static struct event *events;

void read_cb(int fd, short which, void *arg)
{
    int idx = (int) arg, widx = idx + 1;
    u_char ch;

    count += read(fd, &ch, sizeof (ch));

    if (writes)
    {
        if (widx >= num_pipes)
            widx -= num_pipes;
        write(pipes[2 * widx + 1], "e", 1);
        writes--;
        fired++;
    }
}

#else

#include "base/CFdEventMonitor-Inl.h"

CFdEventMonitor *evMonitor = NULL;

void read_cb(int fd, void *ctx, int revents)
{
    int idx = (int) ctx, widx = idx + 1;
    u_char ch;

#ifdef WIN32
    count += recv(fd, (char *) &ch, sizeof (ch), 0);
#else
    count += read(fd, &ch, sizeof (ch));
#endif
    if (writes)
    {
        if (widx >= num_pipes)
            widx -= num_pipes;
#ifdef WIN32
        send(pipes[2 * widx + 1], "e", 1, 0);
#else
        write(pipes[2 * widx + 1], "e", 1);
#endif
        writes--;
        fired++;
    }
}

#endif

void run_once(void)
{
    int *cp, i, space;

#ifndef WIN32
    static struct timeval ta, ts, te;
    gettimeofday(&ta, NULL);
#else
    DWORD start = GetTickCount();
    DWORD start2;
    DWORD end;
#endif

    for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2)
    {
#ifdef LIBEVENT
        event_del(&events[i]);
        event_set(&events[i], cp[0], EV_READ | EV_PERSIST, read_cb, (void *) i);
        event_add(&events[i], 0);
#else
        evMonitor->SetEventWatcher(cp[0], read_cb, (void*) i, FDEVENT_IN);
#endif
    }

#ifdef LIBEVENT
    event_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK);
#else
    evMonitor->Loop(EVENT_LOOP_ONCE | EVENT_LOOP_NONBLOCK);
#endif

    fired = 0;
    space = num_pipes / num_active;
    space = space * 2;
    for (i = 0; i < num_active; i++, fired++)
#ifdef WIN32
        send(pipes[i * space + 1], "e", 1, 0);
#else
        write(pipes[i * space + 1], "e", 1);
#endif

    count = 0;
    writes = num_writes;
    {
        int xcount = 0;
#ifndef WIN32
        gettimeofday(&ts, NULL);
#else
        start2 = GetTickCount();
#endif

        do
        {
#ifdef LIBEVENT
            event_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK);
#else
            evMonitor->Loop(EVENT_LOOP_ONCE | EVENT_LOOP_NONBLOCK);
#endif
            xcount++;
        }
        while (count != fired);

#ifndef WIN32
        gettimeofday(&te, NULL);

#else
        end = GetTickCount();
#endif
        //  if (xcount != count) fprintf(stderr, "Xcount: %d, Rcount: %d\n", xcount, count);
    }
#ifndef WIN32
    timersub(&te, &ta, &ta);
    timersub(&te, &ts, &ts);
    fprintf(stdout, "%8ld %8ld\n", ta.tv_sec * 1000000L + ta.tv_usec, ts.tv_sec * 1000000L + ts.tv_usec);       
#else
    fprintf(stdout, "%d %d\n", end - start, end - start2);    
#endif
}

int
main(int argc, char **argv)
{
#ifdef WIN32
    WSADATA m_WSAData;

    if (WSAStartup(MAKEWORD(2, 2), &m_WSAData) != 0)
    {
        perror("WSAStartup failed");
        exit(0);
    }

#endif

    int i;    
    int *cp;

    num_pipes = 10000;
    num_active = 1000;
    num_writes = num_pipes;

#if 1
    struct rlimit rl;
    rl.rlim_cur = rl.rlim_max = num_pipes * 2 + 50;
    if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
    {
        perror("setrlimit");
    }
#endif

#ifdef LIBEVENT

    if ((events = (event*) calloc(num_pipes, sizeof (struct event))) == NULL)
    {
        perror("malloc");
        exit(1);
    }
    // setenv("EVENT_NOEPOLL", "1", 1);
    // setenv("EVENT_NOPOLL", "1",  1);
    // setenv("EVENT_NOSELECT", "1",  1);
    event_base *base = event_init();
    printf("Method:%s\n", event_base_get_method(base));
#else

#ifndef HAVE_CONSOLE_LOGGER
    CLogger::sharedLogger()->openLog("log.txt", true);
#endif

    evMonitor = new CFdEventMonitor(EnumEventHandlerTypeUnset);

    if (evMonitor->isInitialised() == false)
    {
        perror("Monitor initialization failed");
        exit(1);
    }

    const char **methods = evMonitor->getAvailableMethods();
    for (const char **method = &methods[0]; *method != NULL; method++)
        printf("%s\n", *method);

    printf("Used method:%s\n", evMonitor->getCurrentMethod());

#endif

    if ((pipes = (int*) calloc(num_pipes * 2, sizeof (int))) == NULL)
    {
        perror("malloc");
        exit(1);
    }

    for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2)
    {
#ifdef USE_PIPES
        if (pipe(cp) == -1)
        {
#else

#ifndef WIN32
        if (socketpair(AF_UNIX, SOCK_STREAM, 0, cp) == -1)
#else
        if (dumb_socketpair((SOCKET*) cp, 1) == -1)
#endif
        {
#endif
            perror("pipe");
            exit(1);
        }

#ifndef LIBEVENT
        IFdEventInterface::makeSocketNonBlock(cp[0]);
        IFdEventInterface::makeSocketNonBlock(cp[1]);
#endif

    }

    for (i = 0; i < 10; i++)
    {
        run_once();
    }

    fprintf(stderr, "End\n");

#ifdef WIN32
    Sleep(INFINITE);
#endif

    exit(0);
}
    